<template>
  <view class="base-table" :style="[getTableStyle]" :class="{ 'is-border': border, 'no-data': data.length === 0 }">
    <view class="base-table-inner">
      <view class="base-table-header" v-if="showHeader">
        <view class="b-table" :style="[tableBodyStyle]">
          <view class="b-thead">
            <view class="b-tr" :class="getHeaderClass" :style="getHeaderStyle" @click="handleHeaderClick">
              <view class="b-th" v-if="indexShow" :style="[getIndexColStyle]">
                <view class="b-cell">序号</view>
              </view>
              <view
                class="b-th"
                v-for="item in columns"
                :key="item.fieldName"
                :class="[getCellProps(item).class]"
                :style="[getCellProps(item).style]"
              >
                <view class="b-cell">{{ item.fieldDesc }}</view>
              </view>
            </view>
          </view>
        </view>
      </view>
      <view class="base-table-body">
        <view class="b-table" :style="[tableBodyStyle]">
          <view class="b-tbody" v-if="data.length > 0">
            <view
              class="b-tr"
              v-for="(scope, index) in data"
              :key="index"
              :class="[...getBodyClass(scope, index)]"
              :style="[...getBodyStyle(scope, index)]"
              @click="handleRowClick(scope, index)"
            >
              <view class="b-td" v-if="indexShow" :style="[getIndexColStyle]">
                <view class="b-cell">{{ getIndexMethod(index) }}</view>
              </view>
              <view
                class="b-td"
                v-for="column in columns"
                :key="column.fieldName"
                :class="[getCellProps(column).class]"
                :style="[getCellProps(column).style]"
              >
                <view class="b-cell" @click.stop="handleCellClick(scope, column, index)">
                  <slot name="item" :scope="scope" :column="column" v-if="column.fieldType === 'slot'"></slot>
                  <view v-else>{{ scope[column.fieldName] }}</view>
                </view>
              </view>
            </view>
          </view>
          <view class="base-table-empty" v-else>
            <view class="mt20" v-if="!$slots.empty">{{ emptyText }}</view>
            <slot name="empty"></slot>
          </view>
        </view>
      </view>
      <view class="base-table-footer" v-if="showFooter">
        <view class="b-table" :style="[tableBodyStyle]">
          <view class="b-tbody">
            <view class="b-tr">
              <view class="b-td" v-if="indexShow" :style="[getIndexColStyle]">
                <view class="b-cell">{{ footerText }}</view>
              </view>
              <view
                class="b-td"
                v-for="(item, index) in sumList"
                :key="index"
                :class="[getCellProps(columns[index]).class]"
                :style="[getCellProps(columns[index]).style]"
              >
                <view class="b-cell">{{ item }}</view>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  props: {
    columns: {
      type: Array,
      default: () => []
    },
    data: {
      type: Array,
      default: () => []
    },
    align: {
      type: String,
      default: 'left'
    },
    height: {
      type: String
    },
    maxHeight: {
      type: String
    },
    width: {
      type: String,
      default: '100%'
    },
    emptyText: {
      type: String,
      default: '暂无数据'
    },
    border: {
      type: Boolean,
      default: false
    },
    stripe: {
      type: Boolean,
      default: false
    },
    showHeader: {
      type: Boolean,
      default: true
    },
    showFooter: {
      type: Boolean,
      default: false
    },
    footerMethod: {
      type: Function
    },
    footerText: {
      type: String,
      default: '合计'
    },
    indexShow: {
      type: Boolean,
      default: false
    },
    minItemWidth: {
      type: Number,
      default: 80
    },
    rowClassName: {
      type: [Function, String]
    },
    rowStyle: {
      type: [Function, Object]
    },
    indexMethod: {
      type: Function
    },
    headerRowClassName: {
      type: String
    },
    headerRowStyle: {
      type: Object
    },
    indexWidth: {
      type: String,
      default: '60px'
    }
  },
  data() {
    return {
      sumList: [],
      tableWidth: 0
    };
  },
  mounted() {
    const query = uni.createSelectorQuery().in(this).select('.base-table');
    query
      .boundingClientRect((data) => {
        this.tableWidth = data.width;
      })
      .exec();
  },
  computed: {
    getTableStyle() {
      const { width, height, maxHeight } = this;
      const styleObj = {};
      if (width) {
        styleObj.width = width;
      }
      if (height) {
        styleObj.height = height;
      }
      if (maxHeight) {
        styleObj.maxHeight = maxHeight;
      }
      return styleObj;
    },
    tableBodyStyle() {
      if (!this.tableWidth) return {};
      const clienWidth = this.tableWidth;
      const flexColumn = this.columns.filter((item) => !item.width);
      // set min width
      const minWidth = this.minItemWidth;

      let bodyMinWidth = this.columns.reduce((t, c) => {
        c.width = c.width || minWidth;
        return t + parseFloat(c.width);
      }, 0);
      if (this.indexShow) {
        bodyMinWidth += parseFloat(this.indexWidth);
      }
      if (flexColumn.length > 0 && bodyMinWidth < clienWidth) {
        const flexWidth = clienWidth - bodyMinWidth;
        if (flexColumn.length === 1) {
          flexColumn[0].width = minWidth + flexWidth;
        } else {
          const scaleWidth = flexWidth / flexColumn.length;
          flexColumn.forEach((item) => {
            item.width = minWidth + Math.floor(scaleWidth);
          });
        }
      }
      bodyMinWidth = Math.max(bodyMinWidth, clienWidth);
      return {
        width: `${bodyMinWidth}px`
      };
    },
    showXScroll() {
      const clienWidth = this.tableWidth;
      return clienWidth < parseFloat(this.tableBodyStyle?.width || 0);
    },
    isEmpty() {
      return this.data.length === 0;
    },
    getHeaderClass() {
      const headerClass = [];
      if (this.headerRowClassName) {
        headerClass.push(this.headerRowClassName);
      }
      return headerClass;
    },
    getHeaderStyle() {
      const headerStyle = [];
      if (typeof this.headerRowStyle === 'object') {
        if (this.headerRowStyle) {
          headerStyle.push(this.headerRowStyle);
        }
      }
      return headerStyle;
    },
    getIndexColStyle() {
      return {
        textAlign: this.align,
        width: this.indexWidth
      };
    }
  },
  methods: {
    init() {
      this.sumList = [];
      if (this.showFooter && this.data.length > 0) {
        const { columns, data, footerText } = this;
        if (typeof this.footerMethod === 'function') {
          this.sumList = this.footerMethod({ columns, data });
        } else {
          columns.forEach((column, index) => {
            if (!this.indexShow && index === 0) {
              this.sumList[index] = footerText;
              return;
            }
            const values = data.map((item) => Number(item[column.fieldName]));
            const precisions = [];
            let notNumber = true;
            values.forEach((value) => {
              if (!Number.isNaN(+value)) {
                notNumber = false;
                const decimal = `${value}`.split('.')[1];
                precisions.push(decimal ? decimal.length : 0);
              }
            });
            const precision = Math.max.apply(null, precisions);
            if (!notNumber) {
              this.sumList[index] = values.reduce((prev, curr) => {
                const value = Number(curr);
                if (!Number.isNaN(+value)) {
                  return Number.parseFloat((prev + curr).toFixed(Math.min(precision, 20)));
                } else {
                  return prev;
                }
              }, 0);
            } else {
              this.sumList[index] = '';
            }
          });
        }
      }
    },

    getBodyClass(scope, index) {
      const bodyClass = [];
      if (this.stripe) {
        bodyClass.push({ 'is-stripe': index % 2 === 1 });
      }
      if (typeof this.rowClassName === 'function') {
        const rowClass = this.rowClassName?.(scope, index);
        if (rowClass) {
          bodyClass.push(rowClass);
        }
      } else if (typeof this.rowClassName === 'string') {
        if (this.rowClassName) {
          bodyClass.push(this.rowClassName);
        }
      }
      return bodyClass;
    },

    getBodyStyle(scope, index) {
      const bodyStyle = [];
      if (typeof this.rowStyle === 'function') {
        const rowStyle = this.rowStyle?.(scope, index);
        if (rowStyle) {
          bodyStyle.push(rowStyle);
        }
      } else if (typeof this.rowStyle === 'object') {
        if (this.rowStyle) {
          bodyStyle.push(this.rowStyle);
        }
      }
      return bodyStyle;
    },

    getIndexMethod(index) {
      let curIndex = index + 1;
      if (typeof this.indexMethod === 'function') {
        curIndex = this.indexMethod?.(index);
      }
      return curIndex;
    },

    getCellProps(row) {
      const classList = [];
      if (this.showXScroll && row.fixed) {
        classList.push('fixed');
        if (row.fixed === 'left') {
          classList.push('fixed-left');
        } else {
          classList.push('fixed-right');
        }
      }
      return {
        class: classList,
        style: {
          width: `${row.width}px`,
          textAlign: this.align,
          minWidth: `${this.minItemWidth}px`
        }
      };
    },

    handleHeaderClick() {
      this.$emit('header-click');
    },

    handleRowClick(scope, index) {
      this.$emit('row-click', scope, index);
    },

    handleCellClick(scope, column, index) {
      this.$emit('cell-click', { scope, column, index });
    }
  },
  watch: {
    data: {
      handler() {
        this.init();
      },
      immediate: true,
      deep: true
    }
  }
};
</script>

<style lang="scss" scoped>
@import './basic-table.scss';
</style>
